package org.dreamwork.jasmine2.javac;

import org.apache.log4j.Logger;
import org.dreamwork.compilation.Modifier;
import org.dreamwork.compilation.VirtualClass;
import org.dreamwork.compilation.VirtualField;
import org.dreamwork.compilation.VirtualMethod;
import org.dreamwork.jasmine2.annotation.ParseChildren;
import org.dreamwork.jasmine2.annotation.TemplateParser;
import org.dreamwork.jasmine2.annotation.TemplateSupport;
import org.dreamwork.jasmine2.annotation.WebControlTemplate;
import org.dreamwork.jasmine2.javac.configuration.ConfigurationParser;
import org.dreamwork.jasmine2.javac.configuration.ControlConfiguration;
import org.dreamwork.jasmine2.parser.*;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandler;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by IntelliJ IDEA.
 * User: seth
 * Date: 2009-1-4
 * Time: 17:04:30
 */
public class JasmineCreator2 {
    private Reader reader;
    private Tag root;
    private JasminePageInfo pageInfo;
    private JasmineFileInfo fileInfo;

    private Map<String, Integer> declareControls = new HashMap<String, Integer> ();
    private Map<String, Map<String, Method>> cache = new HashMap<String, Map<String, Method>> ();
    private Map<String, String> userControls = new HashMap<String, String> ();
    private Stack<String> stack = new Stack<String> ();
    private VirtualMethod initComponents/*, dataBind*/;
    private PageInitMethod init;
    private VirtualClass vc = new VirtualClass ();
    private Class superClass;

    private static String compileClassPath;
    private File userControlClassPath;

    private static final Logger logger = Logger.getLogger (JasmineCreator2.class);
    private static final String[] special_attrs = {"runat"};
    private static final Pattern p_context_root = Pattern.compile ("^\"?\\s*~/(.+)\"?$");
//    private static final Pattern p_codelet = Pattern.compile ("^\"?<%=\\s*(.*)?%>(.*)\"?$");

    private static class PageInitMethod extends VirtualMethod {
        private List<String> blocks = new ArrayList<String> ();

        public String toString () {
            StringBuffer buff = new StringBuffer ();
            buff.append ("public void init () {\r\n");
            buff.append ("try {\r\n");
            for (Object block : blocks)
                buff.append (block).append ("\r\n");
            buff.append ("} catch (Exception ex) {\r\n");
            buff.append ("\tthrow new RuntimeException (ex);\r\n");
            buff.append ("}\r\n}");
            return buff.toString ();
        }

        public void addBlock (String block) {
            blocks.add (block);
        }
    }

    public File getUserControlClassPath () {
        return userControlClassPath;
    }

    public void setUserControlClassPath (File userControlClassPath) {
        this.userControlClassPath = userControlClassPath;
    }

    public String getCompileClassPath () {
        return compileClassPath;
    }

    public void setCompileClassPath (String compileClassPath) {
        if (compileClassPath.indexOf ("%20") != -1) compileClassPath = compileClassPath.replaceAll ("%20", " ");
        JasmineCreator2.compileClassPath = compileClassPath;
    }

    public void addUserControl (String name, String className) {
        userControls.put (name, className);
    }

    public void setUserControl (Map<String, String> map) {
        userControls = map;
    }

    public void create (JasmineFileInfo info) throws IOException, ParseException {
        DocumentReader dr = new DocumentReader (reader);
        root = dr.parse ();
        for (int i = 0; i < root.getChildren ().length; i++) {
            Tag tag = root.getChildren ()[i];
            if (tag instanceof PageDirective)
                parsePageInfo ((PageDirective) tag);
        }

        if (pageInfo == null) {
            throw new ParseException ("Can't find page information.");
        }
        fileInfo = info;

        if (pageInfo.getSuperClassName () != null) {
            try {
//                superClass = Class.forName (pageInfo.getSuperClassName ());
                superClass = loadClass (pageInfo.getSuperClassName ());
            } catch (ClassNotFoundException ex) {
                logger.fatal (ex.getMessage (), ex);
                throw new ParseException (ex);
            }
        }

        // init virtual class
        vc.setModifier (Modifier.PUBLIC);
        vc.setName (info.getFileName ());
        vc.setPackageName (info.getPackageName ());
        if (pageInfo.getSuperClassName () == null) {
            if (info.getType () == JasmineFileInfo.TYPE_PAGE) {
                vc.setSuperClassName ("Page");
                vc.addImport ("org.dreamwork.jasmine2.web.controls.Page");
                logger.debug ("preparing to create page");
            } else {
                vc.setSuperClassName ("UserControl");
                vc.addImport ("org.dreamwork.jasmine2.web.controls.UserControl");
                logger.debug ("preparing to create user control");
            }
        } else
            vc.setSuperClassName (pageInfo.getSuperClassName ());
        vc.addImport ("org.dreamwork.peony.DataBinder");
        List list = pageInfo.getImportPackages ();
        for (Object aList : list) {
            String line = (String) aList;
            String[] items = line.split (",");
            for (String item : items) vc.addImport (item.trim ());
        }

        initComponents = new VirtualMethod ();
        initComponents.setModifier (Modifier.PROTECTED);
        initComponents.setReturnType (null);
        initComponents.setName ("initComponents");
        vc.addMethod (initComponents);

        //public void dataBind () throws Throwable
/*
        dataBind = new VirtualMethod ();
        dataBind.setModifier (Modifier.PUBLIC);
        dataBind.addException ("Throwable");
        dataBind.setName ("dataBind");
        dataBind.setReturnType (null);
        vc.addMethod (dataBind);
*/

        VirtualMethod getUniqueId = new VirtualMethod ();
        getUniqueId.setModifier (Modifier.PUBLIC);
        getUniqueId.setName ("getUniqueId");
        getUniqueId.setReturnType ("String");
        getUniqueId.addStatement ("return \"" + info.getClassName () + "\"");
        vc.addMethod (getUniqueId);

        Tag[] children = root.getChildren ();
        stack.push ("this");
        for (Tag child : children) {
//            Tag tag = children [i];
//            if ("html".equalsIgnoreCase (tag.getName ())) continue;
            parseTag (child);
        }

//        dataBind.addStatement ("super.dataBind ()");

        info.createPackageDir ();
        File file = info.getSrcFile ();
        FileOutputStream fos = new FileOutputStream (file);
        try {
            String contentType = pageInfo.getContentType ();
            String enc;
            int pos = contentType.indexOf (";");
            if (pos > 0) {
                enc = contentType.substring (pos + 1).trim ();
                pos = enc.indexOf ('=');
                enc = enc.substring (pos + 1).trim ();
            } else enc = "UTF-8";
            OutputStreamWriter osw = new OutputStreamWriter (fos, enc);
            osw.write (vc.toString ());
            osw.flush ();
        } finally {
            fos.flush ();
            fos.close ();
        }

        StringWriter sw = new StringWriter ();
        PrintWriter pw = new PrintWriter (sw, true);
        if (!compile (file.getAbsolutePath (), info.getClassDir ().getAbsolutePath (), pw)) {
            pw.flush ();
            sw.flush ();
            throw new ParseException (sw.getBuffer ().toString ());
        }
    }

    private void parseTextNode (TextNode tag) {
        if (tag instanceof TaglibDirective) {
            TaglibDirective taglib = (TaglibDirective) tag;
            pageInfo.addUri (taglib.getPrefix (), taglib);
        } else {
            String parentControl = stack.peek ();
            if (parentControl == null) return;
            if (!(tag instanceof JspDirective)) {
                vc.addImport ("org.dreamwork.jasmine2.web.controls.Literal");
                String content = tag.getContent ();
                content = content.replaceAll ("\\r", "\\\\r").replaceAll ("\\n", "\\\\n");
                content = content.replaceAll ("\"", "\\\"");
                initComponents.addStatement (parentControl + ".addControl (new Literal (\"" + content + "\"))");
            } else if (tag instanceof SimpleExpression) {
                String content = tag.getContent ();
                initComponents.addStatement (parentControl + ".addControl (new Literal (" + content + "))");
            } else if (tag instanceof BindingExpression) {
                String content = tag.getContent ();
                StringBuffer buffer = new StringBuffer (parentControl);
                buffer.append (".addControl (new org.dreamwork.jasmine2.web.controls.DynamicControl () {\r\n");
                buffer.append ("\tprotected Object buildContent () throws Throwable {\r\n");
                buffer.append ("\t\treturn ").append (content).append (";\r\n");
                buffer.append ("\t}\r\n");
                buffer.append ("})");
                initComponents.addStatement (buffer.toString ());
            }
        }
    }

    private void parseServerSideScriptNode (Tag tag) {
        Tag[] children = tag.getChildren ();
        if (children != null) for (Tag child : children) {
            JavaCodeSnipParser parser = new JavaCodeSnipParser (child.toString ());
            parser.parse ();
            List<String> classes = parser.getClasses ();
            for (String aClass : classes)
                vc.addCodeSnip (aClass);
            List<String> functions = parser.getFunctions ();
            for (String function : functions)
                vc.addCodeSnip (function);

            List<String> variables = parser.getVariables ();
            for (String variable : variables)
                vc.addCodeSnip (variable);

            List<String> statements = parser.getStatements ();
            if (statements.size () > 0) {
                if (init == null) createInit ();
                for (String statement : statements)
                    init.addBlock (statement);
            }
        }
    }

    private void createInit () {
        init = new PageInitMethod ();
        init.addBlock ("super.init ();\r\n");
        vc.addMethod (init);
    }

    @SuppressWarnings ("unchecked")
    private void parseTag (Tag tag) throws ParseException, IOException {
        if (tag.isHTMLComment () || tag.isJSPComment ()) return;
        boolean runAtServer = "server".equalsIgnoreCase (tag.getAttribute ("runat"));
        if (tag.isTextNode ()) {
            parseTextNode ((TextNode) tag);
            return;
        } else if ("script".equalsIgnoreCase (tag.getName ()) && runAtServer) {
            parseServerSideScriptNode (tag);
            return;
        }
        String id = tag.getAttribute ("id");

        boolean needCreateField = true;
        Field f = null;
        if (superClass != null && id != null/* && runAtServer*/) {
            try {
                f = getField (superClass, id);
                if (f != null) {
                    needCreateField = !(java.lang.reflect.Modifier.isProtected (f.getModifiers ()) ||
                            java.lang.reflect.Modifier.isPublic (f.getModifiers ()));
                }
            } catch (NoSuchFieldException ex) {
                logger.debug ("Can't find field [" + id + "] from super class. create new one. ");
            }
        }

        VirtualField field = createField (tag, id);
        if (needCreateField && runAtServer) { // 定义类变量
            vc.addField (field);
            createInitMethod (field, tag, true);
        } else { // 定义局部变量
            String newString = "()";
            String name = field.getName ();
            if ("org.dreamwork.jasmine2.web.html.GenericHtmlControl".equals (field.getType ()))
                newString = "(\"" + tag.getName () + "\")";
            String stmt = name + " = new " + field.getType () + " " + newString;
            if (f == null) stmt = field.getType () + " " + stmt;
            initComponents.addStatement (stmt);
            createInitMethod (field, tag, false);
        }

        boolean parseChild = true;
        if (tag.getPrefix () != null) {
            String prefix = tag.getPrefix ();
            TaglibDirective taglib = pageInfo.getTaglib (prefix, tag.getName ());
            if (taglib == null)
                throw new ParseException ("Unknown prefix: " + prefix);

            String name = taglib.getName ();
            if (name == null) { // web control
                String uri = taglib.getURI ();
                ControlConfiguration cc = ConfigurationParser.getControl (uri, tag.getName ());
                String className = cc.getClassName ();
                try {
//                    Class c = Class.forName (className);
                    Class c = loadClass (className);
                    TemplateSupport ts = (TemplateSupport) c.getAnnotation (TemplateSupport.class);
                    parseChild = ts == null;
                    if (ts != null) {
                        ParseChildren pc = (ParseChildren) c.getAnnotation (ParseChildren.class);
                        WebControlTemplate wct = (WebControlTemplate) c.getAnnotation (WebControlTemplate.class);
                        Class templateClass = wct.value ();
                        Tag template = getInnerTags (tag);
                        if (template != null) {
                            String content = template.toString ().replace ("\"", "\\\"");
                            content = content.replace ("\n", "\\n").replace ("\r", "\\r");
                            initComponents.addStatement (templateClass.getName () + " " + id + "_template = new " + templateClass.getName () + " ()");
                            initComponents.addStatement ("String " + id + "_content = \"" + content + "\"");
                            initComponents.addStatement (id + "_template.setContent (" + id + "_content)");
                            if (pc != null && pc.value ()) {
                                TemplateParser tp = (TemplateParser) templateClass.getAnnotation (TemplateParser.class);
                                String parserName = tp == null ? "org.dreamwork.jasmine2.template.impl.XMLTemplateParser" : tp.value ().getName ();
                                initComponents.addStatement (parserName + " " + id + "_parser = new " + parserName + " ()");
                                initComponents.addStatement ("if (" + id + "_parser.validateTemplate (" + id + "_content)) " + id + "_template.setRootElement(" + id + "_parser.parse (" + id + "_content))");
    //                            initComponents.addStatement ();
                            }
                            initComponents.addStatement (id + "_template.instantiateIn (" + id + ")");
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace ();
                }
            }            
        }

        if (parseChild) {
            Tag[] children = tag.getChildren ();
            stack.push (field.getName ());
            if (children != null && children.length > 0) {
                for (Tag child : children)
                    parseTag (child);
            }
            if (!stack.empty ()) stack.pop ();
        }
    }

    private Tag getInnerTags (Tag tag) {
        Tag[] children = tag.getChildren ();
        if (children == null || children.length == 0) return null;
        Tag template = new Tag ("template");
        for (Tag child : tag.getChildren ()) template.addChild (child);
        return template;
    }

    private String getFieldName (String className, String id) {
        String fieldName;
        if (id == null) {
            Integer I = declareControls.get (className);
            if (I == null) {
                I = 0;
            } else {
                I = I + 1;
            }
            declareControls.put (className, I);
            int pos = className.lastIndexOf ('.');
            if (pos != -1) fieldName = className.substring (pos + 1) + I;
            else fieldName = className + I;

            fieldName = "_auto_" + Character.toLowerCase (fieldName.charAt (0)) + fieldName.substring (1);
        } else {
            fieldName = id;
        }

        return fieldName;
    }

    @SuppressWarnings ("unchecked")
    private VirtualField createField (Tag child, String id) throws ParseException {
        String className, fieldName;
        String prefix = child.getPrefix ();

/*
        Tag parent = child.getParent ();
        String value = parent.getAttribute ("..isTemplate");
        boolean inTemplate = "true".equals (value);
*/
        if (prefix == null) {
/*
            if (inTemplate) {
                String name = child.getName ();
                vc.addImport ("org.dreamwork.jasmine2.template.impl.BaseTemplate");
                initComponents.addStatement ("BaseTemplate template = new BaseTemplate (\"" + name + "\")");
                Map<String, String> attrs = child.getAttributes ();
                for (String key : attrs.keySet ()) {
                    value = attrs.get (key);
                    initComponents.addStatement ("template.setAttributes (\"" + key + "\", \"" + value + "\")");
                }
                return null;
            } else
*/
            if ("script".equalsIgnoreCase (child.getName ()) && !"server".equalsIgnoreCase (child.getAttribute ("runnat"))) {
                className = "org.dreamwork.jasmine2.web.html.ClientScriptControl";
            } else
                className = "org.dreamwork.jasmine2.web.html.GenericHtmlControl";
        } else {
            TaglibDirective taglib = pageInfo.getTaglib (prefix, child.getName ());
            if (taglib == null)
                throw new ParseException ("Unknown prefix: " + prefix);

            String name = taglib.getName ();
            if (name == null) { // web control
                String uri = taglib.getURI ();
                ControlConfiguration cc = ConfigurationParser.getControl (uri, child.getName ());
                if (cc == null) { // unknown control
                    throw new ParseException ("Unknown control: " + child.getName ());
                } else {
                    className = cc.getClassName ();
                }

/*
                if (cc.isTemplate ()) {
                    child.setAttribute ("..isTemplate", "true");
                }
*/
            } else { // user control
                className = userControls.get (name);
            }
        }

        fieldName = getFieldName (className, id);
        VirtualField vf = new VirtualField ();
        vf.setType (className);
        vf.setName (fieldName);
        vf.setModifier (Modifier.PRIVATE);
        return vf;
    }

    private Map<String, String> dynamicGetters = new HashMap<String, String> ();

    private Map getSetters (VirtualField field) throws IOException {
        Map<String, Method> setters = cache.get (field.getType ());
        try {
            if (setters == null) {
                setters = new HashMap<String, Method> ();
//                ClassLoader loader;
/*
                if (userControlClassPath != null)
                    loader = new JasmineClassLoader (userControlClassPath);
                else
                    loader = getClass ().getClassLoader ();
*/
//                Class clazz = loader.loadClass (field.getType ());
                Class clazz = loadClass (field.getType ());
                Method[] methods = clazz.getMethods ();
                for (Method method : methods) {
                    String methodName = method.getName ();
                    Class[] params = method.getParameterTypes ();
                    if (methodName.startsWith ("set") && params.length == 1) {
                        String x = methodName.substring (3).toLowerCase ();
                        if ("id".equalsIgnoreCase (x)) continue;
                        setters.put (x, method);
                    }
                }
                cache.put (field.getType (), setters);
            }
        } catch (ClassNotFoundException ex) {
            logger.warn (ex.getMessage (), ex);
        }

        return setters;
    }

    private void processDynamicAttribute (String attrName, String value, /*Map setters, String name, */VirtualField field) {
        ExpressionParser parser = new ExpressionParser (value);
        value = parser.parse ();
/*
        if ("datasource".equalsIgnoreCase (attrName)) {
//            Method method = (Method) setters.get (attrName.toLowerCase ().trim ());
            //((WebControl)directControls.getControl ("xx")).setDataSource (yy);
            String stmt = "((org.dreamwork.jasmine2.web.controls.WebControl)directControls.getControl (\"" + name + "\")).setDataSource (" + value + ")";
//            String stmt = name + "." + method.getName () + " (" + value + ")";
            dataBind.addStatement (stmt);
        } else {
*/
            String tempName = dynamicGetters.get (value);
            if (tempName == null) {
                tempName = "dynamicGetters" + (dynamicGetters.size () + 1);
                VirtualMethod vm = new VirtualMethod ();
                vm.setReturnType ("java.lang.Object");
                vm.setName (tempName);
                dynamicGetters.put (value, tempName);
                vm.setModifier (Modifier.PUBLIC);
                vm.addStatement ("return " + value);
                vc.addMethod (vm);
            }
            initComponents.addStatement (field.getName () + ".setDynamicAttribute (\"" + attrName + "\", \"" + tempName + "\")");
//        }
    }

    private void processAttribute (VirtualField field, String name, String attrName, String value, Map setters) {
        if ("dataSource".equalsIgnoreCase (attrName)) {
            initComponents.addStatement (name + ".setDataSource (findDataSource(" + value + "))");
        } else if ("runat".equalsIgnoreCase (attrName)) {
            value = new String (value.toCharArray (), 1, value.length () - 2);
            value = String.valueOf ("server".equalsIgnoreCase (value.trim ()));
            initComponents.addStatement (name + ".setRunAtServer (" + value + ")");
        } else if ("autopostback".equalsIgnoreCase (attrName)) {
            value = new String (value.toCharArray (), 1, value.length () - 2);
            initComponents.addStatement (name + ".setAutoPostback (" + value + ")");
        } else {
            Method method = (Method) setters.get (attrName.toLowerCase ().trim ());
            if (method != null) {
                Class type = method.getParameterTypes ()[0];
                if (isPriType (type)) value = new String (value.toCharArray (), 1, value.length () - 2);
                String stmt = name + "." + method.getName () + " (" + value + ")";
                initComponents.addStatement (stmt);
            } else if (!isSpecialAttribute (attrName))
                initComponents.addStatement (field.getName () + ".setAttribute (\"" + attrName + "\", " + value + ")");
        }
    }

    private void createInitMethod (VirtualField field, Tag tag, boolean createNewStatement) throws IOException {
        boolean isClientScriptControl = "org.dreamwork.jasmine2.web.html.ClientScriptControl".equals (field.getType ());
        boolean isGenericHtmlControl = "org.dreamwork.jasmine2.web.html.GenericHtmlControl".equals (field.getType ());
        String name = field.getName ();
        if (createNewStatement) {
            if (isGenericHtmlControl)
                initComponents.addStatement (name + " = new org.dreamwork.jasmine2.web.html.GenericHtmlControl (\"" + tag.getName () + "\")");
            else
                initComponents.addStatement (name + " = new " + field.getType () + " ()");
        }

        if (isClientScriptControl) {
            Tag[] children = tag.getChildren ();
            if (children != null && children.length > 0) {
                String text = children [0].toString ();
                text = text.replaceAll ("\\\\", "\\\\\\\\").replaceAll ("\"", "\\\\\"");
                initComponents.addStatement (name + ".setInnerText (\"" + text + "\")");
            }
        }

        if (JasmineFileInfo.TYPE_PAGE == fileInfo.getType ())
            initComponents.addStatement (name + ".setId(\"" + field.getName () + "\")");
        else if (JasmineFileInfo.TYPE_USER_CONTROL == fileInfo.getType () && !name.startsWith ("_auto_")) {
            initComponents.addStatement (name + ".setId(getIdPrefix () + \"" + field.getName () + "\")");
        }
        String parentControl = stack.peek ();
        if (parentControl != null)
            initComponents.addStatement (parentControl + ".addControl (" + name + ")");
        if (!name.startsWith ("_auto_") || !isGenericHtmlControl)
            initComponents.addStatement ("directControls.add (\"" + name + "\", " + name + ")");

        String[] attrNames = tag.getAttributeNames ();
        Map setters = getSetters (field);

        if (attrNames != null) for (String attrName : attrNames) {
            String value = tag.getAttribute (attrName).trim ();
            if (value.indexOf ("<%#") != -1) {
                processDynamicAttribute (attrName, value, /*setters, name, */field);
            } else {
                if (value.indexOf ("<%=") != -1) {
                    ExpressionParser parser = new ExpressionParser (value);
                    value = parser.parse ();
                } else {
                    Matcher m = p_context_root.matcher (value);
                    if (m.matches ())
                        value = "request.getContextPath () + \"/" + m.group (1) + "\"";
                    else
                        value = "\"" + value + "\"";
                }

                processAttribute (field, name, attrName, value, setters);
            }
        }

        if ("form".equalsIgnoreCase (tag.getName ()) && "server".equalsIgnoreCase (tag.getAttribute ("runat")))
            initComponents.addStatement ("setServerSideForm (" + name + ")");
        else if ("html".equalsIgnoreCase (tag.getName ()))
            initComponents.addStatement ("html = " + name);
        else if ("body".equalsIgnoreCase (tag.getName ()))
            initComponents.addStatement ("body = " + name);
        else if ("head".equalsIgnoreCase (tag.getName ()))
            initComponents.addStatement ("head = " + name);
    }

    public void setReader (Reader reader) {
        this.reader = reader;
    }

    public Tag getRoot () {
        return root;
    }

    private void parsePageInfo (PageDirective pd) throws ParseException {
        if (pageInfo == null) pageInfo = new JasminePageInfo ();

        if (pd.getAttribute ("buffer") != null) {
            String s_buff_size = pd.getAttribute ("buffer");
            try {
                pageInfo.setBuffSize (Integer.parseInt (s_buff_size));
            } catch (NumberFormatException nfe) {
                throw new ParseException ("Buffer attribute only an interger");
            }
        }
        if (pd.getAttribute ("autoFlush") != null) {
            String s_auto_flush = pd.getAttribute ("autoFlush");
            try {
                pageInfo.setAutoFlush (Boolean.valueOf (s_auto_flush));
            } catch (Exception ex) {
                throw new ParseException ("autoFlush attribute can only be a boolean");
            }
        }
        if (pd.getAttribute ("contentType") != null) {
            if (pageInfo.getContentType () != null) {
                throw new ParseException ("A page only have a content type.");
            }
            pageInfo.setContentType (pd.getAttribute ("contentType"));
        }

        if (pd.getAttribute ("import") != null) {
            pageInfo.addImportPackages (pd.getAttribute ("import"));
        }

        if (pd.getAttribute ("extends") != null) {
            if (pageInfo.getSuperClassName () != null) {
                throw new ParseException ("A page can only extends a class");
            }
            pageInfo.setSuperClassName (pd.getAttribute ("extends"));
        }
    }

    private boolean compile (String srcFileName, String outdir, PrintWriter pw) {
        int ret;
        if (userControlClassPath != null && userControlClassPath.exists ())
            compileClassPath += File.pathSeparator + this.userControlClassPath.getAbsoluteFile ();
        String[] cmd = {"-Xlint:unchecked", "-d", outdir, "-classpath", compileClassPath, "-encoding", "utf-8", srcFileName};
        ret = com.sun.tools.javac.Main.compile (cmd, pw);
        return ret == 0;
    }

    private static boolean isSpecialAttribute (String attrName) {
        for (String special_attr : special_attrs) {
            if (special_attr.equalsIgnoreCase (attrName)) return true;
        }
        return false;
    }

    private boolean isPriType (Class clazz) {
/*
        return clazz == boolean.class || clazz == byte.class || clazz == short.class || clazz == int.class ||
                clazz == long.class || clazz == float.class || clazz == double.class;
*/
        return clazz.isPrimitive ();
    }

    private Field getField (Class c, String name) throws NoSuchFieldException {
        Field f = null;
        try {
            f = c.getDeclaredField (name);
        } catch (NoSuchFieldException sfe) {
            // ignore;
        }
        if (f != null) return f;

        if (c == Object.class) return null;

        return getField (c.getSuperclass (), name);
    }

    private static Class loadClass (String name) throws IOException, ClassNotFoundException {
        try {
            return Class.forName (name);
        } catch (ClassNotFoundException e) {
            // ignore
        }

        if (loader == null) {
            synchronized (locker) {
                if (loader == null) {
                    String[] items = compileClassPath.split (File.pathSeparator);
                    URL[] urls = new URL [items.length];
                    for (int i = 0; i < items.length; i ++) {
                        File file = new File (items [i]);
                        if (file.isDirectory ())
                            urls [i] = new URL (null, (new URL ("file", null, file.getCanonicalPath () + File.separator)).toString (), (URLStreamHandler) null);
                        else
                            urls [i] = new URL (null, (new URL ("file", null, file.getCanonicalFile ().toString ())).toString (), (URLStreamHandler) null);
                    }

                    loader = new URLClassLoader (urls);
                }
            }
        }

        return loader.loadClass (name);
    }

    private static ClassLoader loader = null;
    private static final Object locker = new Object ();
}